package com.roger.myHome.service.impl;

import com.roger.myHome.importExcel.*;
import com.roger.myHome.mapper.ErrorMapper;
import com.roger.myHome.mapper.ImportExcelMapper;
import com.roger.myHome.mapper.InvoiceMapper;
import com.roger.myHome.mapper.InvoicePaymentMapper;
import com.roger.myHome.service.ImportService;
import com.roger.myHome.util.ExcelUtil;
import com.roger.myHome.util.SystemUtil;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.*;

@Service
public class ImportServiceImpl implements ImportService {

    @Resource
    private ImportExcelMapper importExcelMapper;

    @Resource
    private InvoiceMapper invoiceMapper;

    @Resource
    private InvoicePaymentMapper invoicePaymentMapper;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;


    @Resource
    private ErrorMapper errorMapper;

    private final static String contractNoError = "合同编号不存在";

    private final static String serialNoError = "序号不存在";

    private final static String moneyError = "金额错误";

    private final static String invoice = "invoice";

    private final static String remit = "remit";

    /**
     * 导入回款
     * @param file
     * @return
     */
    @Override
    public String importInvoice(MultipartFile file) {
        long begin = System.currentTimeMillis();
        String error = null;
        List<InvoiceImport> excelResult;
        try {
            excelResult = ExcelUtil.readFile(file);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("导入失败解析excel错误");
            return "导入失败解析excel错误";
        }

        try {
            importExcelMapper.deleteErrorLogByType(invoice);
            int errorNum = 0;
            int successNum = 0;
            List<ContractVo> contractList = importExcelMapper.selectContract();
            for (int i = 0; i < excelResult.size(); i++) {
                Integer row = i + 1;
                InvoiceImport excel = excelResult.get(i);
                ContractVo contractVo = getContractVoByNo(contractList, excel.getContractNo().trim());
                if (contractVo == null) {
                    error = "导入失败";
                    errorNum++;
                    ErrorDO errorDO = new ErrorDO();
                    errorDO.setId(row.toString());
                    errorDO.setContractNo(excel.getContractNo().trim());
                    errorDO.setSerialNo(excel.getSerialNo().trim());
                    errorDO.setMoney(excel.getRemitMoney().toString());
                    errorDO.setTime(excel.getCreateTime());
                    errorDO.setMsg(contractNoError);
                    errorDO.setType(invoice);
                    errorMapper.insert(errorDO);
                    continue;
                }
                Integer remitId = importExcelMapper.findRemitId(contractVo.getContractId(),excel.getSerialNo().trim());
                if (remitId == null) {
                    error = "导入失败";
                    errorNum++;
                    ErrorDO errorDO = new ErrorDO();
                    errorDO.setId(row.toString());
                    errorDO.setContractNo(excel.getContractNo().trim());
                    errorDO.setSerialNo(excel.getSerialNo().trim());
                    errorDO.setMoney(excel.getRemitMoney().toString());
                    errorDO.setTime(excel.getCreateTime());
                    errorDO.setMsg(serialNoError);
                    errorDO.setType(invoice);
                    errorMapper.insert(errorDO);
                    continue;
                }

                BigDecimal forecastRemitMoney = importExcelMapper.selectRemitMoneyById(remitId);

                if (forecastRemitMoney.compareTo(excel.getRemitMoney()) < 0) {
                    error = "导入失败";
                    errorNum++;
                    ErrorDO errorDO = new ErrorDO();
                    errorDO.setId(row.toString());
                    errorDO.setContractNo(excel.getContractNo().trim());
                    errorDO.setSerialNo(excel.getSerialNo().trim());
                    errorDO.setMoney(excel.getRemitMoney().toString());
                    errorDO.setTime(excel.getCreateTime());
                    errorDO.setMsg(moneyError);
                    errorDO.setType(invoice);
                    errorMapper.insert(errorDO);
                    continue;
                }

                InvoiceDO invoice = new InvoiceDO();
                invoice.setContractId(contractVo.getContractId());
                invoice.setCreateUser(contractVo.getCreateUser());
                Long invoiceId = SystemUtil.getId();
                invoice.setId(invoiceId);
                invoice.setImportTime(new Date());
                invoice.setApprovalResult(1);
                invoiceMapper.insert(invoice);

                InvoicePaymentDO invoiceRemit = new InvoicePaymentDO();
                invoiceRemit.setContractId(contractVo.getContractId());
                invoiceRemit.setTbContractRemitId(remitId);
                invoiceRemit.setInvoiceMoney(excel.getRemitMoney());
                invoiceRemit.setInvoicedMoney(importExcelMapper.selectInvoicedMoney(remitId));
                invoiceRemit.setSerialNo(excel.getSerialNo());
                Long invoiceRemitId = SystemUtil.getId();
                invoiceRemit.setId(invoiceRemitId);
                invoiceRemit.setInvoiceId(invoiceId);
                invoiceRemit.setImportTime(new Date());
                invoicePaymentMapper.insert(invoiceRemit);
                successNum++;
            }

            if (error != null) {
                System.out.println("导入失败,共有" + errorNum + "条错误记录,成功了" + successNum + "条记录");
                return "导入失败,共有" + errorNum + "条错误记录,成功了" + successNum + "条记录";
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("导入失败:" + e.toString());
            return "导入失败:" + e.toString();
        }

        long end = System.currentTimeMillis();
        return "导入成功:" + (end - begin);
    }

    @Override
    public void importInvoiceCustomize(MultipartFile file) {

        List<InvoiceImport> excelResult;
        try {
            excelResult = ExcelUtil.readFile(file);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("导入失败解析excel错误");
            return;
        }
        List<ErrorDO> errorDOList = Collections.synchronizedList(new ArrayList<>());
        try {
            importExcelMapper.deleteErrorLogByType(invoice);
            //定义一个线程安全的list，防止数据丢失
            List<InvoiceImport> list = Collections.synchronizedList(new ArrayList(excelResult));
            importExcelMapper.deleteErrorLogByType(remit);

            //Executors创建线程池
            ExecutorService taskExecutor = Executors.newCachedThreadPool();

            int count = list.size();
            final CountDownLatch latch = new CountDownLatch(count);//用于判断所有的线程是否结束
            List<ContractVo> contractList = importExcelMapper.selectContract();
            for (int m = 0; m < count; m++) {
                final Integer n = m;//内部类里m不能直接用,所以赋值给n
                Runnable run = new Runnable() {
                    public void run() {
                        try {
                            InvoiceImport excel = list.get(n);
                            if (excel.getContractNo() == null) {
                                return;
                            }
                            ContractVo contractVo = getContractVoByNo(contractList, excel.getContractNo().trim());
                            if (contractVo == null) {
                                String id = (n + 1) + "";
                                ErrorDO errorDO =
                                        new ErrorDO(id,
                                                excel.getContractNo().trim(),
                                                excel.getSerialNo().trim(),
                                                excel.getRemitMoney().toString(),
                                                excel.getCreateTime(),
                                                contractNoError,
                                                invoice);
                                errorDOList.add(errorDO);
                                return;
                            }
                            Integer remitId = importExcelMapper.findRemitId(contractVo.getContractId(),excel.getSerialNo().trim());
                            if (remitId == null) {
                                ErrorDO errorDO = new ErrorDO();
                                errorDO.setId((n + 1) + "");
                                errorDO.setContractNo(excel.getContractNo().trim());
                                errorDO.setSerialNo(excel.getSerialNo().trim());
                                errorDO.setMoney(excel.getRemitMoney().toString());
                                errorDO.setTime(excel.getCreateTime());
                                errorDO.setMsg(serialNoError);
                                errorDO.setType(invoice);
                                errorDOList.add(errorDO);
                                return;
                            }

                            BigDecimal forecastRemitMoney = importExcelMapper.selectRemitMoneyById(remitId);

                            if (forecastRemitMoney.compareTo(excel.getRemitMoney()) < 0) {
                                ErrorDO errorDO = new ErrorDO();
                                errorDO.setId((n + 1) + "");
                                errorDO.setContractNo(excel.getContractNo().trim());
                                errorDO.setSerialNo(excel.getSerialNo().trim());
                                errorDO.setMoney(excel.getRemitMoney().toString());
                                errorDO.setTime(excel.getCreateTime());
                                errorDO.setMsg(moneyError);
                                errorDO.setType(invoice);
                                errorDOList.add(errorDO);
                                return;
                            }

                            InvoiceDO invoice = new InvoiceDO();
                            invoice.setContractId(contractVo.getContractId());
                            invoice.setCreateUser(contractVo.getCreateUser());
                            Long invoiceId = SystemUtil.getId();
                            invoice.setId(invoiceId);
                            invoice.setImportTime(new Date());
                            invoice.setApprovalResult(1);
                            invoiceMapper.insert(invoice);

                            InvoicePaymentDO invoiceRemit = new InvoicePaymentDO();
                            invoiceRemit.setContractId(contractVo.getContractId());
                            invoiceRemit.setTbContractRemitId(remitId);
                            invoiceRemit.setInvoiceMoney(excel.getRemitMoney());
                            invoiceRemit.setInvoicedMoney(importExcelMapper.selectInvoicedMoney(remitId));
                            invoiceRemit.setSerialNo(excel.getSerialNo());
                            Long invoiceRemitId = SystemUtil.getId();
                            invoiceRemit.setId(invoiceRemitId);
                            invoiceRemit.setInvoiceId(invoiceId);
                            invoiceRemit.setImportTime(new Date());
                            invoicePaymentMapper.insert(invoiceRemit);

                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            latch.countDown(); //每次调用CountDown()，计数减1
                        }
                    }
                };
                taskExecutor.execute(run);//开启线程执行池中的任务。还有一个方法submit也可以做到，它的功能是提交指定的任务去执行并且返回Future对象，即执行的结果
            }

            //等待所有线程执行完毕
            latch.await();//主程序执行到await()函数会阻塞等待线程的执行，直到计数为0

            taskExecutor.shutdown();//关闭线程池
            //所有线程执行完毕,执行主线程
            if (!errorDOList.isEmpty()) {
                batchInsertData(errorDOList);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private ContractVo getContractVoByNo(List<ContractVo> contractList, String contractNo) {
        ContractVo contractResult = null;
        for (ContractVo contractVo : contractList) {
            if (contractNo.equals(contractVo.getContractNo())) {
                contractResult = contractVo;
                break;
            }
        }
        return contractResult;
    }

    private void batchInsertData(List<ErrorDO> dataList) {
        // 分批插入数据
        int batchSize = 2000;  // 每批插入的数量

        List<Future<?>> futures = new ArrayList<>();

        for (int i = 0; i < dataList.size(); i += batchSize) {
            List<ErrorDO> subList = dataList.subList(i, Math.min(i + batchSize, dataList.size()));
            futures.add(taskExecutor.submit(new Callable<Void>() {
                @Override
                public Void call() {
                    errorMapper.insertBatchSomeColumn(subList);
                    return null;
                }
            }));
        }

        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }

}
